/**
 * com.chaeron.GPS - An API for access to GPS devices.
 *
 *
 * Copyright (c) 2001, 2002 Chaeron Corporation,
 * All rights reserved.
 * 
 * Chaeron grants you a non-exclusive license to use, modify and re-distribute
 * this program under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version, provided that this copyright notice and 
 * license appear on all copies of the software.
 * 
 * Software is provided "AS IS," without a warranty of any kind. ALL EXPRESS OR
 * IMPLIED REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
 * HEREBY EXCLUDED. THE ENTIRE RISK ARISING OUT OF USING THE SOFTWARE IS ASSUMED
 * BY THE LICENSEE. See the GNU General Public License for more details.
 *
 * CHAERON AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY
 * LICENSEE OR ANY THIRD PARTY AS A RESULT OF USING OR DISTRIBUTING SOFTWARE.
 * IN NO EVENT WILL CHAERON OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE,
 * PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL
 * OR PUNITIVE DAMAGES, HOWEVER CAUSED AN REGARDLESS OF THE THEORY OF LIABILITY,
 * ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF CHAERON HAS
 * BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 * @version     1.02
 * @author      Andrzej Jan Taramina, Chaeron Corporation
 *              andrzej@chaeron.com
 */

package com.chaeron.GPS.Garmin;

//***************************************************************************
//*
//*     Imports
//*
//***************************************************************************/

import com.chaeron.GPS.*;
import com.chaeron.GPS.event.*;
import com.chaeron.GPS.event.decorator.*;
import com.chaeron.GPS.exception.*;


//***************************************************************************
//*
//*     WaypointGarminPacket Class definition
//*
//***************************************************************************/

/**
 * The WaypointGarminPacket class represents Waypoint Data Packets
 *
 */

public class WaypointGarminPacket extends GarminPacket implements GPSWaypoint
{
	//***************************************************************************
	//*
	//*     Constant definitions
	//*
	//***************************************************************************/
	
	//***************************************************************************
	//*
	//*     Attributes
	//*
	//***************************************************************************/
	
	private Protocol	protocol					= null;
	
	private byte		pid							= Protocol.GARMIN_PID_WAYPOINT;
	
	private byte		wptClass					= 0x00;			// User Waypoint
	private byte		colour						= 0;
	private byte		dspl						= 0;
	private byte		attr						= 0x60;
	private short		symbolType					= 0;
	private byte[]		subclass					= { (byte)0x00, (byte)0x00, 
		(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, 
		(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, 
		(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, 
		(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF 
		};
	private int			latsemi						= 0;   
	private int			longsemi					= 0;
	private double		latitude					= 0.0d;   
	private double		longitude					= 0.0d;
	private float		altitude					= 1.0e25f;		// Metres
	private float		depth						= 1.0e25f;		// Metres
	private float		distance					= 0.0f;			// Metres
	private byte[]		state						= { ' ', ' ' };
	private byte[]		country						= { ' ', ' ' };
	private int			ete							= 0xFFFFFFFF;
	private String		identification				= "";
	private String		comment						= "";
	private String		facility					= "";
	private String		city						= "";
	private String		address						= "";
	private String		crossroad					= "";
	private String		linkIdent					= "";
	
	
	//***************************************************************************
	//*
	//*     Constructors
	//*
	//***************************************************************************/
	
	/**
	 * Create a WaypointGarminPacket from the given packet data
	 *
	 * @param	protocol	current protocol
	 * @param	packet		packet data buffer
	 * @param	len			length of packet data
	 * @param	pid			packet ID to use for validation (Protocol.GARMIN_PID_WAYPOINT or Protocol.GARMIN_PID_ROUTE_WAYPOINT)
	 */
	
	WaypointGarminPacket( Protocol protocol, byte[] packet, int len, byte pid  )
	{  
		super( packet, len );
		
		this.protocol 	= protocol;
		this.pid		= pid;
	}
	
	
	//***************************************************************************
	//*
	//*     decode method
	//*
	//***************************************************************************/
	
	/**
	 * Decode the data fields.
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseData() throws GPSPacketException, GPSFunctionUnavailableException, GPSWaypointException
	{
		short		dataProtocol = 0;
		
		switch( pid ) {
				
			case Protocol.GARMIN_PID_WAYPOINT:
				dataProtocol = protocol.getDataProtocol( Protocol.APP_PROTOCOL_WAYPOINT_XFER, Protocol.DATA_PROTOCOL_1 );
				break;
				
			case Protocol.GARMIN_PID_ROUTE_WAYPOINT:
				dataProtocol = protocol.getDataProtocol( Protocol.APP_PROTOCOL_ROUTE_XFER, Protocol.DATA_PROTOCOL_2 );
				break;
		}
		
		switch( dataProtocol ) {
				
			case Protocol.DATA_PROTOCOL_UNDEFINED:
				throw( new GPSWaypointException( GPSWaypointException.EXCEPTION_INVALID_WAYPOINT_DATA_TYPE ) );
				
			case Protocol.DATA_PROTOCOL_D100:
				parseDataD100();
				break;
				
			case Protocol.DATA_PROTOCOL_D101:
				parseDataD101();
				break;
				
			case Protocol.DATA_PROTOCOL_D102:
				parseDataD102();
				break;
				
			case Protocol.DATA_PROTOCOL_D103:
				parseDataD103();
				break;
				
			case Protocol.DATA_PROTOCOL_D104:
				parseDataD104();
				break;
				
			case Protocol.DATA_PROTOCOL_D105:
				parseDataD105();
				break;
				
			case Protocol.DATA_PROTOCOL_D106:
				parseDataD106();
				break;
				
			case Protocol.DATA_PROTOCOL_D107:
				parseDataD107();
				break;
				
			case Protocol.DATA_PROTOCOL_D108:
				parseDataD108();
				break;
				
			case Protocol.DATA_PROTOCOL_D109:
				parseDataD109();
				break;
				
			case Protocol.DATA_PROTOCOL_D150:
				parseDataD150();
				break;
				
			case Protocol.DATA_PROTOCOL_D151:
				parseDataD151();
				break;
				
			case Protocol.DATA_PROTOCOL_D152:
				parseDataD152();
				break;
				
			case Protocol.DATA_PROTOCOL_D154:
				parseDataD154();
				break;
				
			case Protocol.DATA_PROTOCOL_D155:
				parseDataD155();
				break;
				
			default:
				throw( new GPSWaypointException( GPSWaypointException.EXCEPTION_UNSUPPORTED_WAYPOINT_DATA_TYPE ) );
		}	
	}
	
	
	/**
	 * Decode the data fields for a D100 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD100() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 58 ) {  					// Min Packet length For D100 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			byte[]	ident				= new byte[ 6 ];
			byte[]	cmt					= new byte[ 40 ];
			
			ident						= getDataBytes( 0, 6 );
			identification				= new String( ident );
			
			latsemi						= getGarminLong( 6 );
			longsemi					= getGarminLong( 10 );
			
			latitude					= convertSemicirclesToDegrees( latsemi );
			longitude					= convertSemicirclesToDegrees( longsemi );
			
			cmt							= getDataBytes( 18, 40 );
			comment						= new String( cmt );
			
			setMethodsImplemented( 
				WAYPOINT_METHOD_IDENTIFICATION |
				WAYPOINT_METHOD_COMMENT |
				WAYPOINT_METHOD_LATITUDE |
				WAYPOINT_METHOD_LATITUDESEMICIRCLES |
				WAYPOINT_METHOD_LONGITUDE |
				WAYPOINT_METHOD_LONGITUDESEMICIRCLES
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D101 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD101() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 63 ) {  					// Min Packet length For D101 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			parseDataD100();
			
			distance					= getGarminFloat( 58 );		
			symbolType					= getDataByte( 62 );
			
			setMethodsImplemented( 
				getMethodsImplemented() |
				WAYPOINT_METHOD_DISTANCE |
				WAYPOINT_METHOD_SYMBOLTYPE
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D102 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD102() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 64 ) {  					// Min Packet length For D102 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			parseDataD100();
			
			distance					= getGarminFloat( 58 );			
			symbolType					= getGarminInt( 62 );
			
			setMethodsImplemented( 
				getMethodsImplemented() |
				WAYPOINT_METHOD_DISTANCE |
				WAYPOINT_METHOD_SYMBOLTYPE
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D103 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD103() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 60 ) {  					// Min Packet length For D103 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			parseDataD100();
			
			symbolType					= getDataByte( 58 );
			dspl						= getDataByte( 59 );
			
			setMethodsImplemented( 
				getMethodsImplemented() |
				WAYPOINT_METHOD_DISPLAYOPTIONS |
				WAYPOINT_METHOD_SYMBOLTYPE
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D104 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD104() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 65 ) {  					// Min Packet length For D104 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			parseDataD100();
			
			distance 					= getGarminFloat( 58 );
			symbolType					= getGarminInt( 62 );
			dspl						= getDataByte( 64 );
			
			setMethodsImplemented( 
				getMethodsImplemented() |
				WAYPOINT_METHOD_DISTANCE |
				WAYPOINT_METHOD_DISPLAYOPTIONS |
				WAYPOINT_METHOD_SYMBOLTYPE
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D105 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD105() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 11 ) {  					// Min Packet length For D105 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			latsemi						= getGarminLong( 0 );
			longsemi					= getGarminLong( 4 );
			
			latitude					= convertSemicirclesToDegrees( latsemi );
			longitude					= convertSemicirclesToDegrees( longsemi );
			
			symbolType					= getGarminInt( 8 );
			
			identification				= getGarminString( 10 );
			
			setMethodsImplemented( 
				WAYPOINT_METHOD_IDENTIFICATION |
				WAYPOINT_METHOD_LATITUDE |
				WAYPOINT_METHOD_LATITUDESEMICIRCLES |
				WAYPOINT_METHOD_LONGITUDE |
				WAYPOINT_METHOD_LONGITUDESEMICIRCLES |
				WAYPOINT_METHOD_SYMBOLTYPE
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D106 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD106() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 26 ) {  					// Min Packet length For D106 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			int		stridx				= 24;
			
			wptClass					= getDataByte( 0 );
			subclass					= getDataBytes( 1, 13 );
			
			latsemi						= getGarminLong( 14 );
			longsemi					= getGarminLong( 18 );
			
			latitude					= convertSemicirclesToDegrees( latsemi );
			longitude					= convertSemicirclesToDegrees( longsemi );
			
			symbolType					= getGarminInt( 22 );
			
			identification				= getGarminString( stridx );
			stridx						+= identification.length() + 1;
			
			linkIdent					= getGarminString( stridx );
			
			setMethodsImplemented( 
				WAYPOINT_METHOD_IDENTIFICATION |
				WAYPOINT_METHOD_LATITUDE |
				WAYPOINT_METHOD_LATITUDESEMICIRCLES |
				WAYPOINT_METHOD_LONGITUDE |
				WAYPOINT_METHOD_LONGITUDESEMICIRCLES |
				WAYPOINT_METHOD_TYPE |
				WAYPOINT_METHOD_SUBCLASS |
				WAYPOINT_METHOD_SYMBOLTYPE
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D107 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD107()  throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 66 ) {  					// Min Packet length For D107 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			parseDataD100();
			
			symbolType					= getDataByte( 58 );
			dspl						= getDataByte( 60 );
			distance 					= getGarminFloat( 61 );
			colour						= getDataByte( 65 );
			
			setMethodsImplemented( 
				getMethodsImplemented() |
				WAYPOINT_METHOD_DISTANCE |
				WAYPOINT_METHOD_SYMBOLTYPE |
				WAYPOINT_METHOD_DISPLAYOPTIONS |
				WAYPOINT_METHOD_COLOUR
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D108 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD108() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		int		stridx				= 48;
		
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 54 ) {  					// Min Packet length For D108 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			wptClass					= getDataByte( 0 );
			colour						= getDataByte( 1 );
			dspl						= getDataByte( 2 );
			attr						= getDataByte( 3 );
			symbolType					= getGarminInt( 4 );
			subclass					= getDataBytes( 6, 18 );
			
			latsemi						= getGarminLong( 24 );
			longsemi					= getGarminLong( 28 );
			
			latitude					= convertSemicirclesToDegrees( latsemi );
			longitude					= convertSemicirclesToDegrees( longsemi );
			
			altitude					= getGarminFloat( 32 );			
			depth						= getGarminFloat( 36 );	
			distance					= getGarminFloat( 40 );	
			state						= getDataBytes( 44, 2 );
			country						= getDataBytes( 46, 2 );
			
			identification				= getGarminString( stridx );
			stridx						+= identification.length() + 1;
			
			comment						= getGarminString( stridx );
			stridx						+= comment.length() + 1;
			
			facility					= getGarminString( stridx );
			stridx						+= facility.length() + 1;
			
			city						= getGarminString( stridx );
			stridx						+= city.length() + 1;
			
			address						= getGarminString( stridx );
			stridx						+= address.length() + 1;
			
			crossroad					= getGarminString( stridx );
			
			setMethodsImplemented( 
				WAYPOINT_METHOD_TYPE |
				WAYPOINT_METHOD_COLOUR |
				WAYPOINT_METHOD_DISPLAYOPTIONS |
				WAYPOINT_METHOD_ATTRIBUTES |
				WAYPOINT_METHOD_SYMBOLTYPE |
				WAYPOINT_METHOD_SUBCLASS |
				WAYPOINT_METHOD_IDENTIFICATION |
				WAYPOINT_METHOD_LATITUDE |
				WAYPOINT_METHOD_LATITUDESEMICIRCLES |
				WAYPOINT_METHOD_LONGITUDE |
				WAYPOINT_METHOD_LONGITUDESEMICIRCLES |
				WAYPOINT_METHOD_ALTITUDE |
				WAYPOINT_METHOD_DEPTH |
				WAYPOINT_METHOD_DISTANCE |
				WAYPOINT_METHOD_STATECODE |
				WAYPOINT_METHOD_COUNTRYCODE |
				WAYPOINT_METHOD_COMMENT |
				WAYPOINT_METHOD_FACILITY |
				WAYPOINT_METHOD_CITY |
				WAYPOINT_METHOD_ADDRESS |
				WAYPOINT_METHOD_CROSSROAD
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D109 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD109() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		int		stridx				= 52;
		
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 58 ) {  					// Min Packet length For D109 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			wptClass					= getDataByte( 0 );
			colour						= getDataByte( 1 );
			dspl						= getDataByte( 2 );
			attr						= getDataByte( 3 );
			symbolType					= getGarminInt( 4 );
			subclass					= getDataBytes( 6, 18 );
			
			latsemi						= getGarminLong( 24 );
			longsemi					= getGarminLong( 28 );
			
			latitude					= convertSemicirclesToDegrees( latsemi );
			longitude					= convertSemicirclesToDegrees( longsemi );
			
			altitude					= getGarminFloat( 32 );			
			depth						= getGarminFloat( 36 );	
			distance					= getGarminFloat( 40 );	
			state						= getDataBytes( 44, 2 );
			country						= getDataBytes( 46, 2 );
			
			ete							= getGarminLong( 50 );		
			
			identification				= getGarminString( stridx );
			stridx						+= identification.length() + 1;
			
			comment						= getGarminString( stridx );
			stridx						+= comment.length() + 1;
			
			facility					= getGarminString( stridx );
			stridx						+= facility.length() + 1;
			
			city						= getGarminString( stridx );
			stridx						+= city.length() + 1;
			
			address						= getGarminString( stridx );
			stridx						+= address.length() + 1;
			
			crossroad					= getGarminString( stridx );
			
			setMethodsImplemented( 
				WAYPOINT_METHOD_TYPE |
				WAYPOINT_METHOD_COLOUR |
				WAYPOINT_METHOD_DISPLAYOPTIONS |
				WAYPOINT_METHOD_ATTRIBUTES |
				WAYPOINT_METHOD_SYMBOLTYPE |
				WAYPOINT_METHOD_SUBCLASS |
				WAYPOINT_METHOD_IDENTIFICATION |
				WAYPOINT_METHOD_LATITUDE |
				WAYPOINT_METHOD_LATITUDESEMICIRCLES |
				WAYPOINT_METHOD_LONGITUDE |
				WAYPOINT_METHOD_LONGITUDESEMICIRCLES |
				WAYPOINT_METHOD_ALTITUDE |
				WAYPOINT_METHOD_DEPTH |
				WAYPOINT_METHOD_DISTANCE |
				WAYPOINT_METHOD_STATECODE |
				WAYPOINT_METHOD_COUNTRYCODE |
				WAYPOINT_METHOD_ESTIMATEDTIMEENROUTE |
				WAYPOINT_METHOD_COMMENT |
				WAYPOINT_METHOD_FACILITY |
				WAYPOINT_METHOD_CITY |
				WAYPOINT_METHOD_ADDRESS |
				WAYPOINT_METHOD_CROSSROAD
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D150 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD150() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 115 ) {  					// Min Packet length For D150 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			byte[]	ident				= new byte[ 6 ];
			byte[]	cmt					= new byte[ 40 ];
			byte[]  cityb				= new byte[ 24 ];
			byte[]  stateb				= new byte[ 2 ];
			byte[]  facilityb			= new byte[ 30 ];
			
			ident						= getDataBytes( 0, 6 );
			identification				= new String( ident );
			
			country						= getDataBytes( 6, 2 );
			wptClass					= getDataByte( 8 );
			
			
			latsemi						= getGarminLong( 9 );
			longsemi					= getGarminLong( 13 );
			
			latitude					= convertSemicirclesToDegrees( latsemi );
			longitude					= convertSemicirclesToDegrees( longsemi );
			
			altitude					= getGarminInt( 17 );
			
			cityb						= getDataBytes( 19, 24 );
			city						= new String( cityb );
			
			state						= getDataBytes( 43, 2 );
			
			facilityb					= getDataBytes( 45, 30 );
			facility					= new String( facilityb );
			
			cmt							= getDataBytes( 75, 40 );
			comment						= new String( cmt );
			
			setMethodsImplemented( 
				WAYPOINT_METHOD_TYPE |
				WAYPOINT_METHOD_IDENTIFICATION |
				WAYPOINT_METHOD_LATITUDE |
				WAYPOINT_METHOD_LATITUDESEMICIRCLES |
				WAYPOINT_METHOD_LONGITUDE |
				WAYPOINT_METHOD_LONGITUDESEMICIRCLES |
				WAYPOINT_METHOD_ALTITUDE |
				WAYPOINT_METHOD_STATECODE |
				WAYPOINT_METHOD_COUNTRYCODE |
				WAYPOINT_METHOD_COMMENT |
				WAYPOINT_METHOD_FACILITY |
				WAYPOINT_METHOD_CITY
			);
		}
	}
	
	
	/**
	 * Decode the data fields for a D151 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD151() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 125 ) {  					// Min Packet length For D151 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			byte[]  cityb				= new byte[ 24 ];
			byte[]  stateb				= new byte[ 2 ];
			byte[]  facilityb			= new byte[ 30 ];
			
			parseDataD100();
			
			distance					= getGarminFloat( 58 );		
			
			facilityb					= getDataBytes( 62, 30 );
			facility					= new String( facilityb );
			
			cityb						= getDataBytes( 92, 24 );
			city						= new String( cityb );
			
			state						= getDataBytes( 116, 2 );
			
			altitude					= getGarminInt( 118 );
			
			country						= getDataBytes( 120, 2 );
			wptClass					= getDataByte( 124 );
			
			setMethodsImplemented( 
				getMethodsImplemented() |
				WAYPOINT_METHOD_DISTANCE |
				WAYPOINT_METHOD_FACILITY |
				WAYPOINT_METHOD_CITY |
				WAYPOINT_METHOD_STATECODE |
				WAYPOINT_METHOD_ALTITUDE |
				WAYPOINT_METHOD_COUNTRYCODE |
				WAYPOINT_METHOD_TYPE
			);
		}
	}
	
	/**
	 * Decode the data fields for a D152 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD152() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 125 ) {  					// Min Packet length For D152 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			parseDataD151();
		}
	}
	
	/**
	 * Decode the data fields for a D154 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD154() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 127 ) {  					// Min Packet length For D154 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			parseDataD151();
			
			symbolType				= getGarminInt( 125 );
			
			setMethodsImplemented( 
				getMethodsImplemented() |
				WAYPOINT_METHOD_SYMBOLTYPE
			);
		}
	}
	
	/**
	 * Decode the data fields for a D155 Data Protocol
	 *
	 * @throws GPSPacketException
	 * @throws GPSFunctionUnavailableException
	 */
	
	public void parseDataD155() throws GPSPacketException, GPSFunctionUnavailableException
	{  
		if( getPacketIDByte() != protocol.getProtocolPacketID( pid ) ) {
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETID_INVALID ) );
		} else if( getDataLength() < 128 ) {  					// Min Packet length For D155 only!!!
			throw( new GPSPacketException( GPSPacketException.EXCEPTION_PACKETLENGTH_INVALID ) );
		} else {
			parseDataD154();
			
			dspl				= getDataByte( 127 );
			
			setMethodsImplemented( 
				getMethodsImplemented() |
				WAYPOINT_METHOD_DISPLAYOPTIONS
			);
		}
	}
	
	
	//***************************************************************************
	//*
	//*     Packet Processing Methods
	//*
	//***************************************************************************/
	
	/**
	 * Process the packet
	 *
	 * @param	gps			the GPS object that requested the packet to be processed
	 */
	
	void process( GPS gps )
	{
		GPSValue		gpsValue = this;
		
		if( !isParsed() ) {
			try {
				parse();
				if( Compile.INCLUDE_EVENT_DECORATORS ) {
					GPSWaypointDecorator	decorator = new GPSWaypointDecorator( this );
					decorator.addMethodsImplemented( this.getMethodsImplemented() );
					gpsValue = decorator;
				}
				gps.postGPSEvent( new GPSEvent( GPSEvent.GPS_EVENT_WAYPOINT, gpsValue ) );
			}
			catch( GPSException exception ) {
				gps.postGPSEvent( new GPSEvent( GPSEvent.GPS_EVENT_ERROR, toString(), exception ) );
			}
		}
	}
	
	
	/**
	 * Get events generated.
	 *
	 * @return		return a valid event type(s) that will be generated by this packet
	 */
	
	static int getEventsGenerated()
	{  
		return( GPSEvent.GPS_EVENT_ERROR | GPSEvent.GPS_EVENT_WAYPOINT );
	}
	
	
	//***************************************************************************
	//*
	//*    Accessor methods
	//*
	//***************************************************************************/
	
	/**
	 * Get the Waypoint Type
	 *
	 * @return		Waypoint Type
	 */
	
	public byte getType()
	{  
		return( wptClass );
	}
	
	
	/**
	 * Get the Waypoint Colour
	 *
	 * @return		Waypoint Colour
	 */
	
	public byte getColour()
	{  
		return( colour );
	}
	
	
	/**
	 * Get the Waypoint Display Options
	 *
	 * @return		Waypoint Display Options
	 */
	
	public byte getDisplayOptions()
	{  
		return( dspl );
	}
	
	
	/**
	 * Get the Waypoint Attributes
	 *
	 * @return		Waypoint Attributes
	 */
	
	public byte getAttributes()
	{  
		return( attr );
	}
	
	
	/**
	 * Get the Waypoint Symbol Type
	 *
	 * @return		Waypoint Symbol Type
	 */
	
	public short getSymbolType()
	{  
		return( symbolType );
	}
	
	
	/**
	 * Get the Waypoint Subclass
	 *
	 * @return		Waypoint Subclass
	 */
	
	public byte[] getSubclass()
	{  
		return( subclass );
	}
	
	
	/**
	 * Get the Latitude (degrees)
	 *
	 * @return		Latitude (degrees)
	 */
	
	public double getLatitude()
	{  
		return( latitude );
	}
	
	
	/**
	 * Get the Longitude (degrees)
	 *
	 * @return		Longitude (degrees)
	 */
	
	public double getLongitude()
	{  
		return( longitude );
	}
	
	
	/**
	 * Get the Latitude (Semicircles)
	 *
	 * @return		Latitude (Semicircles)
	 */
	
	public int getLatitudeSemicircles()
	{  
		return( latsemi );
	}
	
	
	/**
	 * Get the Longitude (Semicircles)
	 *
	 * @return		Longitude (Semicircles)
	 */
	
	public int getLongitudeSemicircles()
	{  
		return( longsemi );
	}
	
	
	/**
	 * Get the Altitude (metres).  A value of 1.0e25 means the parameter is unsupported or unknown.
	 *
	 * @return		Altitude (metres)
	 */
	
	public float getAltitude()
	{  
		return( altitude );
	}
	
	
	/**
	 * Get the Depth (metres). A value of 1.0e25 means the parameter is unsupported or unknown.
	 *
	 * @return		Depth (metres)
	 */
	
	public float getDepth()
	{  
		return( depth );
	}
	
	
	/**
	 * Get the Distance (metres). A value of 1.0e25 means the parameter is unsupported or unknown.
	 *
	 * @return		Distance (metres)
	 */
	
	public float getDistance()
	{  
		return( distance );
	}
	
	
	/**
	 * Get the State Code
	 *
	 * @return		State Code
	 */
	
	public String getStateCode()
	{  
		String value = new String( state );
		
		if( value.equals( "  " ) ) {
			value = "";
		}
		
		return( value );
	}
	
	
	/**
	 * Get the Country Code
	 *
	 * @return		Country Code
	 */
	
	public String getCountryCode()
	{  
		String value = new String( country );
		
		if( value.equals( "  " ) ) {
			value = "";
		}
		
		return( value );
	}
	
	
	/**
	 * Get the Estimated Time Enroute
	 *
	 * @return		ETE
	 */
	
	public int getEstimatedTimeEnroute()
	{  
		return( ete );
	}
	
	
	/**
	 * Get the Identification String
	 *
	 * @return		Identification String
	 */
	
	public String getIdentification()
	{  
		return( identification );
	}
	
	
	/**
	 * Get the Comment String
	 *
	 * @return		Comment String
	 */
	
	public String getComment()
	{  
		return( comment );
	}
	
	
	/**
	 * Get the Facility String
	 *
	 * @return		Facility String
	 */
	
	public String getFacility()
	{  
		return( facility );
	}
	
	
	/**
	 * Get the City String
	 *
	 * @return		City String
	 */
	
	public String getCity()
	{  
		return( city );
	}
	
	
	/**
	 * Get the Address String
	 *
	 * @return		Address String
	 */
	
	public String getAddress()
	{  
		return( address );
	}
	
	
	/**
	 * Get the Crossroad String
	 *
	 * @return		Crossroad String
	 */
	
	public String getCrossroad()
	{  
		return( crossroad );
	}
	
	/**
	 * Get the Link Identification String
	 *
	 * @return		Link Identification String
	 */
	
	public String getLinkIdentification()
	{  
		return( linkIdent );
	}
	
}
